package kr.ac.ssu.ailab.Control;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Random;
import java.util.Vector;
import kr.ac.ssu.ailab.Model.Data.Cell;
import kr.ac.ssu.ailab.Model.Data.Cluster;
import kr.ac.ssu.ailab.View.SeqTest;

/**
 * 클러스터를 컨트롤 하는 클래스
 * 
 * @author Rivers
 * 
 */
public class ClusterControl
{
	public static final int NUM_CANDIDATE = 5;

	private static ClusterControl clusterControl = null;
	private Cluster[][] clusters = null;;
	private Cluster startCluster = null;
	private Cluster finishCluster = null;
	private boolean isCreated = false;
	private static int numX = 0;
	private static int numY = 0;
	private Vector<Cell[][]> includedCellList = null;
	private Vector<ArrayList<Cell>> candiCells = null;

	/**
	 * 클러스터 컨트롤의 생성자
	 */
	private ClusterControl()
	{
		this.clusters = null;
		this.startCluster = null;
		this.finishCluster = null;
		this.isCreated = false;
		this.numX = 0;
		this.numY = 0;
		this.includedCellList = null;
		this.candiCells = null;

	}

	/**
	 * 싱글톤 구현을 위한 클러스터 컨트롤 인스턴스를 반환하는 메소드
	 * 
	 * @return
	 */
	public static ClusterControl getInstance()
	{
		if (clusterControl == null)
			clusterControl = new ClusterControl();

		return clusterControl;
	}

	/**
	 * 클러스터의 인스턴스를 null로 set 하는 메소드
	 * 
	 */
	public static void setNullInstance()
	{
		if (clusterControl != null)
			clusterControl = null;
	}

	/**
	 * Lv1 클러스터를 구성하는 인스턴스를 get 하는 메소드
	 * 
	 * @return
	 */
	public Cluster[][] getClusterArray()
	{
		return clusters;
	}

	/**
	 * 클러스터 내에 있는 StandCell에 대한 후보군 셀들을 get 하는 method
	 * 
	 * @return
	 */
	public Vector<ArrayList<Cell>> getCandiCells()
	{
		return candiCells;
	}

	/**
	 * StandCell을 set 하는 method
	 * 
	 * @param candiCells
	 */
	public void setCandiCells(Vector<ArrayList<Cell>> candiCells)
	{
		this.candiCells = candiCells;
	}

	/**
	 * Index에 따른 클러스터를 get 하는 메소드
	 * 
	 * @param index
	 * @return
	 */
	public Cluster getCluster(int index)
	{

		try
		{
			for (int i = 0; i < numX; i++)
				for (int j = 0; j < numY; j++)
					if (clusters[i][j].getSeqCluster() == index)
					{

						return clusters[i][j];
					}
		}
		catch (NullPointerException e)
		{
			if (clusters == null)
				System.err.println("CLUSTERS NULL");

			System.err.println(e);
		}
		return null;

	}

	/**
	 * 클러스터의 X 사이즈를 set 하는 메소드
	 * 
	 * @param X
	 */
	public void setNumX(int X)
	{
		this.numX = X;
	}

	/**
	 * 클러스터의 Y 사이즈를 set 하는 메소드
	 * 
	 * @param Y
	 */
	public void setNumY(int Y)
	{
		this.numY = Y;
	}

	/**
	 * 클러스터의 X 사이즈를 get 하는 메소드
	 * 
	 * @return
	 */
	public int getNumX()
	{
		return this.numX;
	}

	/**
	 * 클러스터의 Y 사이즈를 get 하는 메소드
	 * 
	 * @return
	 */
	public int getNumY()
	{
		return this.numY;
	}

	/**
	 * 클러스터를 생성하는 메소드
	 */
	public void createCluster()
	{
		clusters = new Cluster[numX][numY];
		@SuppressWarnings("unused")
		int clusterNum = 0;
		this.includedCellList = new Vector<Cell[][]>();
		for (int i = 0; i < numY; i++)
			for (int j = 0; j < numX; j++)
			{
				clusters[j][i] = new Cluster(new Point(j, i), 1);
				this.includedCellList.add(clusterNum, SeqTest.mapControl.getClusterCellArray(j, i));
				clusters[j][i].setSeqCluster(clusterNum++);
			}

		SeqTest.mapControl.setCount(0);

	}

	/**
	 * Lv1 Cluster 가 생성되었는지 안되었는지 유무여부를 set 하는 메소드
	 * 
	 * @param isCreated
	 */
	public void setCreated(boolean isCreated)
	{
		this.isCreated = isCreated;
	}

	/**
	 * Lv1 Cluster 가 생성되었는지 안되었는지 유무여부를 확인하는 메소드
	 * 
	 * @return
	 */
	public boolean isCreated()
	{
		return this.isCreated;
	}

	/**
	 * 시작점을 포함한 클러스터를 get 하는 메소드
	 * 
	 * @return
	 */
	public Cluster getStartCluster()
	{
		return startCluster;
	}

	/**
	 * 시작점을 포함한 클러스터를 set 하는 메소드
	 * 
	 * @param x
	 * @param y
	 */
	public void setStartCluster(int x, int y)
	{

		this.startCluster = clusters[x][y];
	}

	/**
	 * 목표점을 포함한 클러스터를 get 하는 메소드
	 * 
	 * @return
	 */
	public Cluster getFinishCluster()
	{
		return finishCluster;
	}

	/**
	 * 목표점을 포함한 클러스터를 set 하는 메소드
	 * 
	 * @param x
	 * @param y
	 */
	public void setFinishCluster(int x, int y)
	{
		this.finishCluster = clusters[x][y];
	}

	/**
	 * Lv1 클러스터의 근접한 클러스터를 get 하는 메소드
	 * 
	 * @param now
	 * @return
	 */
	public Cluster[] getAdjacentCluster(Cluster now)
	{

		ArrayList<Cluster> nextList = new ArrayList<Cluster>();
		Cluster next[] = null;
		Point p = now.getPosition();
		// �ΰ� ó��
		if (p.y != 0)
		{
			nextList.add(clusters[p.x][p.y - 1]);
		}
		if (p.x != getNumX() - 1)
		{
			nextList.add(clusters[p.x + 1][p.y]);
		}
		if (p.y != getNumY() - 1)
		{
			nextList.add(clusters[p.x][p.y + 1]);
		}
		if (p.x != 0)
		{
			nextList.add(clusters[p.x - 1][p.y]);
		}

		if (p.x != 0 && p.y != 0)
		{
			nextList.add(clusters[p.x - 1][p.y - 1]);
		}
		if (p.x != getNumX() - 1 && p.y != 0)
		{
			nextList.add(clusters[p.x + 1][p.y - 1]);
		}
		if (p.x != 0 && p.y != getNumY() - 1)
		{
			nextList.add(clusters[p.x - 1][p.y + 1]);
		}
		if (p.x != getNumX() - 1 && p.y != getNumY() - 1)
		{
			nextList.add(clusters[p.x + 1][p.y + 1]);
		}

		next = new Cluster[nextList.size()];

		for (int i = 0; i < nextList.size(); i++)
		{
			next[i] = nextList.get(i);
		}

		return next;
	}

	/**
	 * 동적 이벤트 발생시 부분적으로 클러스터 내에서 CSC 를 생성하는 메소드
	 */
	public void createPartialCandiCSC(int number)
	{

		int numOfCellsInClusterX = SeqTest.mapControl.getMapNumX() / getNumX();
		int numOfCellsInClusterY = SeqTest.mapControl.getMapNumY() / getNumY();
		int failedCount = 0;
		ArrayList<Cell> tempList = new ArrayList<Cell>();
		Random random = new Random();
		Cell temp = null;

		Cell center = includedCellList.get(number)[numOfCellsInClusterX / 2][numOfCellsInClusterY / 2];

		for (int j = 0; j < NUM_CANDIDATE; j++)
		{

			if (!center.isObstacle() && !tempList.contains(center)
					&& center.getType() != CellControl.START_CELL
					&& center.getType() != CellControl.FINISH_CELL)
			{
				tempList.add(center);
				center.setType(CellControl.CANDI_STAND_CELL);
				j++;
			}
			/*
			 * Generate random fashioned Candidate Cells inside of the range of
			 * a Cell
			 */
			int x = Math.abs(random.nextInt() % numOfCellsInClusterX);
			int y = Math.abs(random.nextInt() % numOfCellsInClusterY);
			temp = includedCellList.get(number)[x][y];
			if (!temp.isObstacle() && temp.getType() != CellControl.START_CELL
					&& temp.getType() != CellControl.FINISH_CELL && temp != center
					&& !tempList.contains(temp))
			{
				tempList.add(temp);
				temp.setType(CellControl.CANDI_STAND_CELL);

			}
			if (temp.isObstacle() && tempList.size() != NUM_CANDIDATE)
			{
				failedCount++;
				j--;
			}

			candiCells.set(number, tempList);

			tempList = new ArrayList<Cell>();
		}

	}

	/**
	 * 클러스터 전체의 CSC 를 생성하는 메소드
	 */
	public void createCandiCSC()
	{
		//
		int numOfCellsInClusterX = SeqTest.mapControl.getMapNumX() / getNumX();
		int numOfCellsInClusterY = SeqTest.mapControl.getMapNumY() / getNumY();

		candiCells = new Vector<ArrayList<Cell>>();
		int failedCount = 0;
		ArrayList<Cell> tempList = new ArrayList<Cell>();
		Random random = new Random();
		Cell temp = null;

		for (int i = 0; i < getNumX() * getNumY(); i++)
		{

			Cell center = includedCellList.get(i)[numOfCellsInClusterX / 2][numOfCellsInClusterY / 2];

			for (int j = 0; j < NUM_CANDIDATE; j++)
			{
				if (!center.isObstacle() && !tempList.contains(center)
						&& center.getType() != CellControl.START_CELL
						&& center.getType() != CellControl.FINISH_CELL)
				{
					tempList.add(center);
					center.setType(CellControl.CANDI_STAND_CELL);
					j++;

				}
				/*
				 * Generate random fashioned Candidate Cells inside of the range
				 * of a Cell
				 */
				int x = Math.abs(random.nextInt() % numOfCellsInClusterX);
				int y = Math.abs(random.nextInt() % numOfCellsInClusterY);
				//				
				temp = includedCellList.get(i)[x][y];

				if (!temp.isObstacle() && temp.getType() != CellControl.START_CELL
						&& temp.getType() != CellControl.FINISH_CELL && temp != center
						&& !tempList.contains(temp))
				{
					tempList.add(temp);
					temp.setType(CellControl.CANDI_STAND_CELL);

				}
				if (temp.isObstacle() && tempList.size() != NUM_CANDIDATE)
				{
					failedCount++;
					j--;
				}

			}
			candiCells.add(i, tempList);
			tempList = new ArrayList<Cell>();

		}

	}

	/**
	 * 클러스터를 null 로 설정 하는 메소드
	 */
	public void clearCluster()
	{
		clusters = null;
		isCreated = false;
	}

	/**
	 * CSC 를 Clear 하는 메소드 
	 */
	public void clearCSC()
	{
		// int tempSize = 0;
		for (int i = 0; i < candiCells.size(); i++)
		{
			int tempSize = ((ArrayList<Cell>) candiCells.get(i)).size();
			System.err.println(tempSize);
			for (int j = 0; j < tempSize; j++)
			{
				candiCells.get(i).get(j).setType(CellControl.NORMAL);
				// candiCells.get(i).get(j).repaint();
			}
		}
	}

}
